package com.panfeng.xcloud.gateway.filters;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.panfeng.xcloud.common.core.constants.Constants;
import com.panfeng.xcloud.common.core.enums.ResponseStatusEnum;
import com.panfeng.xcloud.common.core.utils.RequestUtil;
import com.panfeng.xcloud.common.core.utils.StringUtils;
import com.panfeng.xcloud.common.core.utils.UuidUtils;
import com.panfeng.xcloud.common.core.web.vo.ResponseUtils;
import com.panfeng.xcloud.gateway.config.AntiCrawlerPropConfig;
import com.panfeng.xcloud.gateway.reptile.Checker;
import com.panfeng.xcloud.gateway.reptile.UrlRsaAntiCrawlerRule;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 鉴权信息传递ZuulFilter过滤器
 *
 * @author xiaobo
 * @date 2018-12-15
 */
@Slf4j
@Component
public class HttpAuthFilter extends ZuulFilter {

	static {
		//添加反爬虫规则链,日后可以针对其他反爬虫规则进行扩展，实现特定的反爬虫规则即可
		UrlRsaAntiCrawlerRule urlRsaAntiCrawlerRule = new UrlRsaAntiCrawlerRule();
		Checker.addRuleStrategy(urlRsaAntiCrawlerRule);
	}

	@Autowired
	private AntiCrawlerPropConfig antiCrawlerPropConfig;

	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}

	@Override
	public int filterOrder() {
		return 0;
	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	/**
	 * 设置log上下文
	 *
	 * @param requestContext
	 */
	private void putLogContext(RequestContext requestContext){
		HttpServletRequest request = requestContext.getRequest();

		String xd_request_id = request.getHeader("xd-request-id");
		xd_request_id = StringUtils.isEmpty(xd_request_id) ? "": xd_request_id;

		String xd_Agent = request.getHeader("xd-agent");
		xd_Agent = StringUtils.isEmpty(xd_Agent) ? "": xd_Agent;

		String Xd_VersionName = request.getHeader("xd-version-name");
		Xd_VersionName = StringUtils.isEmpty(Xd_VersionName) ? "": Xd_VersionName;

		String Xd_VersionCode = request.getHeader("xd-version-code");
		Xd_VersionCode = StringUtils.isEmpty(Xd_VersionCode) ? "": Xd_VersionCode;

		String generateUUID = UuidUtils.generateUUID() + System.currentTimeMillis();
		MDC.put(Constants.MDC_KEY, "xd-version-code:" + Xd_VersionCode +  ",xd-version-name:" + Xd_VersionName + ",xd-agent:" + xd_Agent + ",xd_request_id:" + xd_request_id + ",uuid_" + generateUUID);
	}

	@Override
	public Object run() {
		log.info(">>> 开始-网关路由 <<<");
		RequestContext requestContext = RequestContext.getCurrentContext();
		//设置log上下文
		putLogContext(requestContext);
		TimerCounter.before(requestContext.getRequest());
		try {
			doRunFilter(requestContext);
		} catch (Exception e) {
			log.error(">>> 网关路由拦截失败，异常信息EXCEPTION:{} <<<", e.getMessage(), e);
		}
		return null;
	}

	private void doRunFilter(RequestContext requestContext) throws IOException {
		HttpServletRequest request = requestContext.getRequest();
		String requestURI = request.getRequestURI();

		if("true".equalsIgnoreCase(antiCrawlerPropConfig.getEnabled())){
			if(requestURI.indexOf("swagger") != -1
					|| requestURI.indexOf("api-docs") != -1
					|| requestURI.indexOf("checkLoginName") != -1
					|| requestURI.indexOf("oauth2login") != -1
					|| requestURI.indexOf("oauth") != -1
					|| requestURI.indexOf("token") != -1
					|| requestURI.indexOf("login") != -1
					|| requestURI.indexOf("loginToken") != -1
					|| requestURI.indexOf("/auth/openid") != -1
					|| requestURI.indexOf("/auth/verifyCodeLogin") != -1
					|| requestURI.indexOf("/auth/pwdLogin") != -1
					|| requestURI.indexOf("/user/info/userinfo") != -1
					|| requestURI.indexOf("/sys/user/userinfo") != -1){
				log.info(">>> 请求接口url:{} 当前请求为特殊的api请求，不进行反爬虫验证 <<<" ,requestURI);
			}else{
				if(!Checker.doCheck(request)){
					log.info(">>> 反爬虫拦截==> 请求接口url:{} 验证失败直接返回 <<<" ,requestURI);
					responseHandler(requestContext,ResponseStatusEnum.ANTI_CRAWLER_FAIL);
					return;
				}
			}
		}

		String authHeader = RequestUtil.getAuthHeader(request);
		if(StringUtils.isNotEmpty(authHeader) && StringUtils.isNotEmpty(authHeader.substring(7))){
			boolean tokenFormatFlag = checkTokenFormat(authHeader.substring(7));
			log.info(">>> 请求接口url:{} 当前请求包含AUTHORIZATION内容 <<<" ,requestURI);
			if(tokenFormatFlag){
				log.info(">>> 当前AUTHORIZATION的值:{} ,将路由传递到各个微服务上去!!! <<<", authHeader);

				requestContext.addZuulRequestHeader("x-label", authHeader);
			}
			//添加请求上下文参数
			addSettingReqContextParam(requestContext);
			return;
		}

		String tokenValue = request.getParameter("token");
		if(!StringUtils.isEmpty(tokenValue)){
			log.info(">>> 请求接口url:{} 当前请求url后包含token <<<" ,requestURI);
			boolean tokenFormatFlag = checkTokenFormat(authHeader.substring(7));
			if(tokenFormatFlag){
				authHeader = Constants.BEARER_TOKEN_TYPE + tokenValue;
				requestContext.addZuulRequestHeader(HttpHeaders.AUTHORIZATION, authHeader);
				log.info(">>> 当前AUTHORIZATION的值:{} ,将路由传递到各个微服务上去!!! <<<", authHeader);

				requestContext.addZuulRequestHeader("x-label", authHeader);
			}
		}
		//添加请求上下文参数
		addSettingReqContextParam(requestContext);
		return;
	}

	/**
	 * 添加请求上下文参数
	 *
	 * @param requestContext
	 */
	private void addSettingReqContextParam(RequestContext requestContext){
		HttpServletRequest request = requestContext.getRequest();

		String xd_request_id = request.getHeader("xd-request-id");
		xd_request_id = StringUtils.isEmpty(xd_request_id) ? "": xd_request_id;

		String xd_Agent = request.getHeader("xd-agent");
		xd_Agent = StringUtils.isEmpty(xd_Agent) ? "": xd_Agent;

		String Xd_VersionName = request.getHeader("xd-version-name");
		Xd_VersionName = StringUtils.isEmpty(Xd_VersionName) ? "": Xd_VersionName;

		String Xd_VersionCode = request.getHeader("xd-version-code");
		Xd_VersionCode = StringUtils.isEmpty(Xd_VersionCode) ? "": Xd_VersionCode;

		requestContext.addZuulRequestHeader("xd-request-id", xd_request_id);
		requestContext.addZuulRequestHeader("xd-agent", xd_Agent);
		requestContext.addZuulRequestHeader("xd-version-name", Xd_VersionName);
		requestContext.addZuulRequestHeader("xd-version-code", Xd_VersionCode);
	}

	private boolean checkTokenFormat(String token){
		if(StringUtils.isEmpty(token)){
			log.error(">>> 前端传递的token值为空<<<");
			return false;
		}
		try {
			JSONObject.parse(token);
		}catch (Exception e){
			log.error(">>> 前端传递的token值是非JSON格式的值，不符合jwt的要求<<<");
			return false;
		}
		return true;
	}

	/**
	 * 返回逻辑统一处理
	 *
	 * @param ctx
	 * @param responseStatusEnum
	 * @throws IOException
	 */
	private void responseHandler(RequestContext ctx, ResponseStatusEnum responseStatusEnum) throws IOException {
		ObjectMapper mapper = new ObjectMapper();
		ctx.setSendZuulResponse(false);
		HttpServletResponse httpResponse = ctx.getResponse();
		httpResponse.setCharacterEncoding("UTF-8");
		httpResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
		httpResponse.setStatus(HttpServletResponse.SC_OK);
		httpResponse.getWriter().write(mapper.writeValueAsString(ResponseUtils.getResponseByStatus(responseStatusEnum)));
		ctx.setResponse(httpResponse);
	}

}
